/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// cvar.c -- dynamic variable tracking

#include "quakedef.h"

char	*cvar_null_string = "";

/*
============
Cvar_FindVar
============
*/
cvar_t *Cvar_FindVar (char *name)
{
	clistentry_t *found = Cmd_FindInCompleteList (name);

	if (found && found->type == clist_cvar)
		return (cvar_t *) found->data;
	else return NULL;
}

/*
============
Cvar_VariableValue
============
*/
float Cvar_VariableValue (char *var_name)
{
	cvar_t *var = Cvar_FindVar (var_name);

	if (!var)
		return 0;

	return Q_atof (var->string);
}


/*
============
Cvar_VariableString
============
*/
char *Cvar_VariableString (char *var_name)
{
	cvar_t *var = Cvar_FindVar (var_name);

	if (!var)
		return cvar_null_string;

	return var->string;
}


void Cvar_Update (cvar_t *var, char *value)
{
	var->string = CopyString (value);
	var->value = Q_atof (var->string);
	var->integer = Q_rint (var->value);
	var->boolean = !!var->integer;
}


void Cvar_Changed (cvar_t *var)
{
	if (var->flags & CVAR_SERVER)
		if (sv.active)
			SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string);

	// prevent the callback from being called recursively in case it range-checks the cvar and does another set
	if (var->callback && !var->recursive)
	{
		var->recursive = TRUE;
		var->callback ();
		var->recursive = FALSE;
	}
}


/*
============
Cvar_Set
============
*/
void Cvar_Set (cvar_t *var, char *value)
{
	BOOL changed;

	if (!var)
	{
		// there is an error in C code if this happens
		Con_Printf (PRINT_DEFAULT, "Cvar_Set: variable not found\n");
		return;
	}

	changed = !!Q_strcmp (var->string, value);

	// free the old value string and update the values
	zoneHeap->Free (var->string);
	var->string = NULL;

	Cvar_Update (var, value);

	// notify of a change
	if (changed) Cvar_Changed (var);
}

void Cvar_SetValue (cvar_t *var, float value)
{
	Cvar_Set (var, va ("%g", value));
}

void Cvar_SetA_f (void)
{
	cvar_t *var = NULL;

	if (Cmd_Argc () != 3)
		return;

	if ((var = Cvar_FindVar (Cmd_Argv (1))) != NULL)
	{
		// mark for archiving and set the cvar
		var->flags |= (CVAR_ARCHIVE | CVAR_SETA);
		Cvar_Set (var, Cmd_Argv (2));
	}
}


/*
============
Cvar_Command

Handles variable inspection and changing from the console
============
*/
BOOL Cvar_Command (cvar_t *v)
{
	// check variables
	if (!v)
	{
		// if Cmd_Argc is 2 then register the cvar with default flags and callback
		// this allows us to exec the configs early (before cvars are registered during normal startup)
		// then have normal registration come along and update their properties
		if (Cmd_Argc () == 2)
			v = Cvar_Get (Cmd_Argv (0), Cmd_Argv (1));
		else return FALSE;
	}

	// perform a variable print or set
	if (Cmd_Argc () == 1)
		Con_Printf (PRINT_DEFAULT, "\"%s\" is \"%s\"\n", v->name, v->string);
	else Cvar_Set (v, Cmd_Argv (1));

	return TRUE;
}


/*
============
Cvar_WriteVariables

Writes lines containing "set variable value" for all variables
with the archive flag set to TRUE.
============
*/
void Cvar_WriteVariables (FILE *f)
{
	// sort the completion list if it's dirty
	Cmd_CheckList ();

	for (int i = 0; i < cmd_numcompletelist; i++)
	{
		if (cmd_completelist[i].type == clist_cvar)
		{
			cvar_t *var = (cvar_t *) cmd_completelist[i].data;

			if (var->flags & CVAR_SETA)
				fprintf (f, "seta %s \"%s\"\n", var->name, var->string);
			else if (var->flags & CVAR_ARCHIVE)
				fprintf (f, "%s \"%s\"\n", var->name, var->string);
		}
	}
}


cvar_t *Cvar_Get (char *var_name, char *var_value, int flags, xcommand_t callback)
{
	// try to find the variable
	cvar_t *var = Cvar_FindVar (var_name);

	if (!var)
	{
		// allocate a new cvar
		var = (cvar_t *) zoneHeap->Alloc (sizeof (cvar_t));

		// allocate it's name
		var->name = CopyString (var_name);

		// update the values
		Cvar_Update (var, var_value);

		// add it to the completion list
		Cmd_AddToCompleteList (var->name, var, clist_cvar);
	}

	// update the cvar
	if (flags) var->flags |= flags;
	if (callback) var->callback = callback;

	// see did the cvar change
	if (strcmp (var->string, var_value))
		Cvar_Changed (var);

	// and now return the cvar we got
	return var;
}


cvar_t *Cvar_NextServerCvar (char *prev)
{
	int i;

	// sort the completion list if it's dirty
	Cmd_CheckList ();

	for (i = 0; i < cmd_numcompletelist; i++)
	{
		// assume it's a cvar
		cvar_t *var = (cvar_t *) cmd_completelist[i].data;

		// make sure that it really is one
		if (cmd_completelist[i].type != clist_cvar) continue;

		// server cvars only please
		if (!(var->flags & CVAR_SERVER)) continue;

		// skip over the previous one
		if (prev && prev[0])
			if (!stricmp (prev, var->name)) continue;

		// this is now the next one
		return var;
	}

	// didn't find it
	return NULL;
}

